﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data.Common;
using System.Globalization;
using Visifire.Charts;

namespace ResourceBlender_Express {
    /// <summary>
    /// Shows the completion of resources used by an application on a graph.
    /// </summary>
    public partial class WinCompletionSummary : Window {
        /// <summary>
        /// Gets or sets IDs of the applications for which to show a completion summary.
        /// </summary>
        /// <value>The application ids.</value>
        public string[] ApplicationIds { get; set; }

        private Style styHeading;

        /// <summary>
        /// Initializes a new <see cref="WinCompletionSummary"/>.
        /// </summary>
        public WinCompletionSummary() {
            InitializeComponent();
            styHeading = FindResource("HeadingFont") as Style;
        }

        public WinCompletionSummary(string[] applicationIds) 
            : this() {
            this.ApplicationIds = applicationIds;
        }

        /// <summary>
        /// Loads the list of unique languages when the window loads.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            RefreshSummary();
        }

        private void RefreshSummary() {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                Database db = null;
                List<string> errors = new List<string>();
                try {
                    db = new Database();
                    db.OpenConnection();
                    List<ApplicationSummary> resourceSummaries = new List<ApplicationSummary>();
                    string cmdApplicationInfo = string.Format("SELECT displayname, defaultLanguage, languages FROM {0}applications WHERE id = @app", db.TablePrefix),
                           cmdBundles = string.Format("SELECT name, (SELECT COUNT(bundle) FROM {0}elements AS e WHERE e.bundle = b.id) AS number FROM {0}bundles AS b WHERE application = @app", db.TablePrefix),
                           cmdCompletion = string.Format("SELECT COUNT(language) FROM {0}elements AS e LEFT JOIN {0}resources AS r ON e.resourceid = r.resourceid WHERE language = @abbrev AND applicationid = @app", db.TablePrefix);
                    using(Database.Command cmd = db.CreateCommand()) {
                        cmd.AddParameterWithValue("@app", "");
                        cmd.AddParameterWithValue("@abbrev", "");
                        foreach(string appId in ApplicationIds) {
                            cmd.CommandText = cmdApplicationInfo;
                            cmd.Parameters["@app"].Value = appId;
                            try {
                                DbDataReader reader = cmd.ExecuteReader();
                                reader.Read();
                                string[] langCodes = reader["languages"].ToString().Split(',');
                                string displayName = reader["displayname"].ToString(),
                                       defaultLang = reader["defaultlanguage"].ToString();
                                reader.Close();
                                ApplicationSummary summary = new ApplicationSummary(displayName);

                                cmd.CommandText = cmdBundles;
                                reader = cmd.ExecuteReader();
                                string bundleFormat = "{0} ({1} element{2})";
                                while(reader.Read()) {
                                    try {
                                        int numElements = reader.GetInt32(1);
                                        summary.Bundles.Add(string.Format(bundleFormat, reader["name"].ToString(), numElements, numElements == 1 ? "" : "s"));
                                    } catch {
                                        errors.Add(string.Format("Could not read bundle for {0}: {1}", displayName, reader[0].ToString()));
                                    }
                                }
                                reader.Close();

                                cmd.CommandText = cmdCompletion;
                                cmd.Parameters["@abbrev"].Value = defaultLang;
                                try {
                                    summary.TotalElements = int.Parse(cmd.ExecuteScalar().ToString());
                                } catch {
                                    throw new Exception("Error reading the total number of elements");
                                }

                                // the map is sorted by the english name of the languages it contains, but the abbreviation is needed to find the flag image
                                foreach(string abbrev in langCodes) {
                                    cmd.Parameters["@abbrev"].Value = abbrev;
                                    int numTranslations = 0;
                                    string englishName = null,
                                           flag = null;
                                    try {
                                        numTranslations = int.Parse(cmd.ExecuteScalar().ToString());
                                        CultureInfo info = CultureInfo.GetCultureInfo(abbrev);
                                        englishName = info.EnglishName;
                                        flag = Utils.GetISO3166(info);
                                    } catch(Exception ex) {
                                        englishName = abbrev;
                                        errors.Add("Could not parse culture for " + abbrev + ": " + ex.Message);
                                    }
                                    summary.CompletionMap.Add(new LanguageCompletion(englishName, flag, numTranslations)); // famfamfam's flags comply with ISO3166 thankfully
                                }
                                summary.CompletionMap.Sort(new Comparison<LanguageCompletion>(delegate(LanguageCompletion a, LanguageCompletion b) {
                                    return b.EnglishName.CompareTo(a.EnglishName);
                                }));
                                resourceSummaries.Add(summary);
                            } catch(Exception ex) {
                                errors.Add(ex.Message);
                            }
                        }
                        dwe.Result = resourceSummaries;
                    }
                        /* //todo: summary of all applications/average completion?
                         * if(plcDynamic.Controls.Count > 1) {
                            StringBuilder sqlBuffer = new StringBuilder("SELECT COUNT(language) FROM ").Append(db.TablePrefix).Append("elements AS e LEFT JOIN ").Append(db.TablePrefix).Append("resources AS r ON e.resourceid = r.resourceid WHERE language = @abbrev"+db.GenerateInClause("applicationid", ,true));
                            cmd.CommandText = sqlBuffer.ToString();
                            cmd.Parameters["@abbrev"].Value = defaultLang;
                        }*/
                } catch(Exception ex) {
                    errors.Add("Error connecting to the database: " + ex.Message);
                } finally {
                    if(db != null) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
                if(errors.Count > 0) {
                    throw new MultipleException(errors);
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Error is MultipleException) {
                    MultipleException ex = rwe.Error as MultipleException;
                    if(ex.Errors.Count == 1) {
                        DialogBox.Show(this, ex.Errors[0], "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                    } else {
                        DialogBox.Show(this, ex, "Warning", DialogBox.DialogBoxIcon.Warning);
                    }
                }
                if(rwe.Result is List<ApplicationSummary>) { // exception might not have been fatal
                    List<ApplicationSummary> summaries = rwe.Result as List<ApplicationSummary>;
                    if(summaries.Count == 0)
                        return;
                    Style invisibleListBox = FindResource("InvisibleListBox") as Style;
                    foreach(ApplicationSummary summary in summaries) {
                        ListBox lst = new ListBox();
                        lst.Style = invisibleListBox;
                        ScrollViewer.SetHorizontalScrollBarVisibility(lst, ScrollBarVisibility.Disabled);
                        ScrollViewer.SetCanContentScroll(lst, false);
                        lst.Items.Add(summary);

                        Chart chart = new Chart();
                        lst.Items.Add(chart);
                        chart.MinWidth = 350;
                        chart.MinHeight = 20 * summary.CompletionMap.Count;
                        chart.ScrollingEnabled = false;
                        chart.Watermark = false;
                        Title title = new Title();
                        title.Text = "Translation Completion";
                        title.FontWeight = FontWeights.Bold;
                        chart.Titles.Add(title);
                        Axis xAxis = new Axis(),
                             yAxis = new Axis();
                        xAxis.Title = "Language";
                        xAxis.AxisType = AxisTypes.Secondary;
                        yAxis.Title = "Completion (%)";
                        yAxis.TitleFontWeight = FontWeights.Bold;
                        yAxis.AxisMinimum = 0;
                        yAxis.AxisMaximum = 100;
                        yAxis.AxisType = AxisTypes.Primary;
                        chart.AxesX.Add(xAxis);
                        chart.AxesY.Add(yAxis);
                        chart.PlotArea.IsHitTestVisible = false;
                        DataSeries series = new DataSeries();
                        series.RenderAs = RenderAs.Bar;
                        series.IsHitTestVisible = false;
                        chart.Series.Add(series);
                        foreach(LanguageCompletion language in summary.CompletionMap) {
                            DataPoint point = new DataPoint();
                            point.IsHitTestVisible = false;
                            int completionPercentage = (int)Math.Round((language.NumTranslations / (double)summary.TotalElements) * 100.0, 0);
                            if(completionPercentage > 100) {
                                completionPercentage = 100;
                            } else if(completionPercentage < 0) {
                                completionPercentage = 0;
                            }
                            point.YValue = completionPercentage;
                            point.ToolTipText = string.Format("{0} - {1}%\n{2}/{3} translations", language.EnglishName, completionPercentage, language.NumTranslations,summary.TotalElements);
                            point.LegendText = language.EnglishName;
                            point.AxisXLabel = language.EnglishName;
                            point.LabelText = "      ";
                            point.LabelStyle = LabelStyles.OutSide;
                            point.LabelEnabled = true;
                            if(language.Image != null) {
                                ImageBrush brush = new ImageBrush(new BitmapImage(new Uri("pack://application:,,/Resources/images/flags/" + language.Image + ".png", UriKind.RelativeOrAbsolute)));
                                brush.Stretch = Stretch.None;
                                brush.AlignmentX = AlignmentX.Right;
                                brush.AlignmentY = AlignmentY.Center;
                                point.LabelBackground = brush;
                            }
                            series.DataPoints.Add(point);
                        }

                        TabItem tab = new TabItem();
                        tbcSummaries.Items.Add(tab);
                        tab.Header = summary.DisplayName;
                        if(summaries.Count == 1) {
                            tab.Height = 0;
                        }
                        tab.Content = lst;
                    }
                    tbcSummaries.SelectedIndex = 0;
                }
            };
            SetStatus("Loading languages...", true);
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Shows the help file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            Utils.OpenHelp("Creating.Variants.html");
        }

        /// <summary>
        /// Sets the text on the status message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="showProgress"><c>true</c> if the progress bar should be shown, otherwise <c>false</c>.</param>
        private void SetStatus(string message, bool showProgress) {
            prgStatus.Visibility = showProgress ? Visibility.Visible : Visibility.Hidden;
            if(message == null) {
                lblStatus.Visibility = Visibility.Hidden;
            } else {
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Content = message;
            }
        }
    }
}
